﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using C4F.VistaP2P.Common;

namespace C4F.VistaP2P.Win.File
{

    /// <summary>
    /// Use this control to send a file to the other nodes in the mesh.
    /// </summary>
    [Description("Use this control with the NetworkManager to share files"),
     ToolboxBitmap(typeof(resfinder), "C4F_P2PWinformControls.c4f.bmp"),
    Designer(typeof(FileDesigner))] //this designer attribute enables us to have smart tags on the control
    public partial class FileControl : UserControl
    {
        /// <summary>
        /// Constructor, that just initializes the control and registers for the send button click.
        /// </summary>
        public FileControl()
        {
            InitializeComponent();
            btnSendFile.Click += new EventHandler(_SendFileButton_Click);         
        }

        #region public methods and events to use when completely overriding this control
        /// <summary>
        /// send a file to the other nodes in the network.
        /// </summary>
        /// <example>
        ///  mFile.SendFile("C:\Users\Rob\myTextFile.txt");
        /// </example>
        /// <param name="fullPathAndFileName">the fullpath and filename</param>
        public void SendFile(string fullPathAndFileName)
        {
            //send the file in another thread so the UI thread remains responsive...  
            if (fullPathAndFileName != null && System.IO.File.Exists(fullPathAndFileName))
            {
                BackgroundWorker worker;
                worker = new System.ComponentModel.BackgroundWorker();
                worker.DoWork += new System.ComponentModel.DoWorkEventHandler(_SendFileBGWorker);
                worker.RunWorkerAsync(fullPathAndFileName);
            }
            else
            {
                throw new ApplicationException(string.Format("File not found: {0}", fullPathAndFileName));
            }
        }

        /// <summary>
        /// This event is raised when a File is received from the mesh.
        /// </summary>
        public event EventHandler<FileChangedEventArgs> FileReceived;

        /// <summary>
        /// This event is raised when the status of the network has changed.
        /// </summary>
        public event EventHandler<StatusChangedEventArgs> StatusChanged;


        #endregion

        #region Properties

        //A place to store the custom button if assigned.
        private Button mSendFileButton = null;


        /// <summary>
        /// The SendFileButton Button property.  Set this to another button if you wish to customize the control some.
        /// The Attributes (Description and Category) expose the property on the Winforms design surface.
        /// The get method returns the custom button if set (mSendFileButton), or the default one (btnSendFile) if one
        /// is not set.
        /// <value></value>
        /// </summary>
        [
           Description(""),
           Category("P2P/controls")
        ]
        public Button SendFileButton
        {
            get
            {                
                if (mSendFileButton == null)
                    return btnSendFile;
                else
                    return mSendFileButton;
            }
            set
            {
                //if we previously registered for a button, unregister for it.
                if (mSendFileButton != null)
                {
                    mSendFileButton.Click -= new EventHandler(_SendFileButton_Click);
                }

                mSendFileButton = value;
                if (mSendFileButton != null)
                {
                    //unregister for the default event
                    btnSendFile.Visible = false;
                    btnSendFile.Click -= new EventHandler(_SendFileButton_Click);

                    //register for the new controls event                    
                    mSendFileButton.Click += new EventHandler(_SendFileButton_Click);
                    
                }
                else
                {
                    btnSendFile.Visible = true;
                    btnSendFile.Click += new EventHandler(_SendFileButton_Click);
                    Show();
                }
            }
        }


        /// <summary>
        /// a place to store the custom status log.
        /// </summary>
        private TextBox mStatusLog = null;

        /// <summary>
        /// The StatusLog TextBox property.  Set this to another textbox if you wish to have this control updated with 
        /// the status log.
        /// The Attributes (Description and Category) expose the property on the Winforms design surface.
        /// The get method returns the custom textbox if set (mStatusLog), or the default one (tbStatus) if one
        /// is not set.
        /// <value></value>
        /// </summary>
        [
           Description(""),
           Category("P2P/controls")
        ]
        public TextBox StatusLog
        {
            get
            {
                if (mStatusLog == null)
                    return tbStatus;
                else
                    return mStatusLog;
            }
            set
            {
                mStatusLog = value;
                if (value != null)
                {
                    tbStatus.Visible = false;                    
                }
                else
                {
                    this.Show();
                    tbStatus.Visible = true;
                }
            }
        }

        #endregion

        
        /// <summary>
        ///Have to shadow the visible property for smart tag.. 
        /// </summary>
        internal bool ShadowVisible
        {
            get
            {
                return Visible;
            }
            set
            {
                Visible = value;
            }
        }

        
        /// <summary>
        /// Handle the button click and open a dialog to send a file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _SendFileButton_Click(object sender, EventArgs e)
        {
            Microsoft.Win32.OpenFileDialog ofd;
            ofd = new Microsoft.Win32.OpenFileDialog();
            ofd.FileName = "openFileDialog1";
            ofd.Filter = "All Files (*.*)|*.*";
            ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            ofd.RestoreDirectory = true;
            ofd.Title = "Select a File";

            bool ok = (bool)ofd.ShowDialog();
            if (ok)
            {
                SendFile(ofd.FileName);
            }            
        }

        /// <summary>
        /// The NetworkManager control subscribes to this event so it can send a file to the mesh.
        /// </summary>
        internal event EventHandler<FileChangedEventArgs> SendingFile;

        /// <summary>
        /// _UpdateStatusLogHandler delegate used to ensure that the control is only changed by the thread that created it.
        /// search online for crossthreading issues for more information.
        /// </summary>
        /// <param name="message">the message to put into the log</param>
        private delegate void _UpdateStatusLogHandler(string message);
        void UpdateStatusLog(string message)
        {
            if (message != null)
            {
                if (StatusLog.InvokeRequired)
                {
                    StatusLog.Invoke(new _UpdateStatusLogHandler(UpdateStatusLog), message);
                }
                else
                {
                    StatusLog.Text += message;
                }
            }
        }

        /// <summary>
        /// The background worker that sends out the file.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">The argument is the string of the current node's user name.</param>
        private void _SendFileBGWorker(object sender, DoWorkEventArgs e)
        {
           UpdateStatusLog(String.Format("Sending a File: {0}\r\n", (String)e.Argument));

            System.IO.Stream stream = System.IO.File.OpenRead((String)e.Argument);
            if (stream != null)
            {
                Packet packet = new Packet();
                packet.stream = stream;
                packet.fileName = System.IO.Path.GetFileName((String)e.Argument);
                MetadataFileInfo md = new MetadataFileInfo();
                md.ReadMetaData((String)e.Argument);
                packet.fileName = (String)e.Argument;

                if (SendingFile != null)
                {
                    SendingFile(this, new FileChangedEventArgs(packet));
                }
            }
            UpdateStatusLog("File Sent\r\n");          
        }




        /// <summary>
        /// _FileReceivedHandler delegate used to ensure that the control is only changed by the thread that created it.
        /// search online for crossthreading issues for more information.
        /// </summary>
        /// <param name="FileChangedEventArgs">the message to put into the log</param>
        private delegate void _FileReceivedHandler(FileChangedEventArgs e);
        public void SetFileReceived(FileChangedEventArgs e)
        {

            //push us back to the UI thread so if we have to raise an event FileReceived it's on the controls thread...
            if (StatusLog.InvokeRequired)
            {
                StatusLog.Invoke(new _FileReceivedHandler(SetFileReceived), e);
            }
            else
            {
                if (FileReceived != null)
                {
                    //let someone else handle it because they registered for the event..
                    FileReceived(this, e);
                }
                else
                {
                    //handle it ourselves
                    Stream stream = null;
                    try
                    {
                        DialogResult dr = System.Windows.Forms.MessageBox.Show(e.Packet.senderNodeName + " just sent you a file. Select OK to accept it, otherwise select Cancel", "A file was sent to you!", MessageBoxButtons.OKCancel);
                        if (dr == DialogResult.OK)
                        {
                            Microsoft.Win32.SaveFileDialog ofs;
                            ofs = new Microsoft.Win32.SaveFileDialog();
                            ofs.FileName = e.Packet.fileName;
                            ofs.Filter = "All Files (*.*)|*.*";
                            ofs.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                            ofs.RestoreDirectory = true;
                            ofs.FileName = e.Packet.fileName;
                            ofs.Title = e.Packet.senderNodeName + "Sent you a file.";

                            bool ok = (bool)ofs.ShowDialog();
                            if (ok)
                            {
                                stream = new FileStream(ofs.FileName, FileMode.Create, FileAccess.Write);
                                StreamingHelper.CopyStream(e.Packet.stream, stream);
                            }
                        }
                    }
                    catch (Exception)
                    {

                    }
                    finally
                    {
                        if (stream != null)
                            stream.Close();
                    }
                }
            }
        }

        
        /// <summary>
        /// A delegate to let us invoke the function on the controls thread.
        /// </summary>
        /// <param name="userName">The username the networkmanager is using</param>
        /// <param name="StatusChangedEventArgs">used to determin the status of the network</param>
        private delegate void _StatusChangedHandler(string userName, StatusChangedEventArgs e);
        
        /// <summary>
        /// Set by the NetworkManager, this lets the control know what state the network is in.
        /// </summary>
        /// <param name="StatusChangedEventArgs">used to determine the status of the network</param>
        internal void SetStatusChanged(string userName, StatusChangedEventArgs e)
        {
            
            if (btnSendFile.InvokeRequired)
            {
                btnSendFile.Invoke(new _StatusChangedHandler(SetStatusChanged), new object[] { userName, e });
            }
            else
            {
                if (userName == e.Member && e.NewNodeJoined)
                {
                    SendFileButton.Enabled = true;
                }

                if (userName == e.Member && e.NodeLeft)
                {
                    SendFileButton.Enabled = false;
                }

                //let anyone else know if they registered for the event.
                if (null != StatusChanged)
                {
                    StatusChanged(userName, e);
                }
            }
        }


        /// <summary>
        /// A delegate to let us invoke the function on the controls thread.
        /// </summary>
        /// <param name="state"></param>
        private delegate void _SetConnectionStatusHandler(SystemState state);
        /// <summary>
        /// Enable or disable the Filebox based on our connection status, and use a delegate so we are thread safe
        /// and only make changes to the control on the UI control's thread.
        /// </summary>
        /// <param name="state"></param>
        internal void SetConnectionStatus(SystemState state)
        {
            if (SendFileButton.InvokeRequired)
            {
                SendFileButton.Invoke(new _SetConnectionStatusHandler(SetConnectionStatus), state);
            }
            else
            {
                if (state == SystemState.LoggedIn)
                {
                    SendFileButton.Enabled = true;
                }
                else if (state == SystemState.LoggedOut)
                {
                   SendFileButton.Enabled = false;
                }
            }
        }
    }
}
